#include "preHeader.h"
#include "MapServerSession.h"
#include "MapServerSessionHandler.h"
#include "CenterSession.h"
#include "SocialServerSession.h"
#include "MapServerMgr.h"
#include "Game/AccountMgr.h"
#include "Game/CharacterMgr.h"
#include "Game/TeleportMgr.h"
#include "Mail/MailMgr.h"
#include "Team/TeamMgr.h"
#include "Manager/ActivityObjectMgr.h"
#include "Manager/GuildLeagueMgr.h"
#include "GameServer.h"

MapServerSession::MapServerSession(uint32 sn)
: RPCSession("MapServerSession", MS_RPC_INVOKE_RESP)
, m_sn(sn)
, m_service(0)
, m_gsIdx(0)
, m_serviceId(0)
, m_pProxy(NULL)
{
	sCharUpdateInfoBitMask.Resize((int)CharUpdateInfo::Type::Count);
}

MapServerSession::~MapServerSession()
{
	SAFE_DELETE(m_pProxy);
}

int MapServerSession::HandlePacket(INetPacket *pck)
{
	if (IsReady() || pck->GetOpcode() < FLAG_MAP2GAME_MSG_NEED_REGISTER_BEGIN) {
		return sMapServerSessionHandler.HandlePacket(this, *pck);
	} else {
		return SessionHandleUnhandle;
	}
}

void MapServerSession::OnShutdownSession()
{
	WLOG("Close MapServerSession [sn:%u serviceId:%u gsIdx:%u].",
		m_sn, m_serviceId, m_gsIdx);
	if (m_pProxy != NULL) {
		sMapServerMgr.RemoveMapServer(m_pProxy);
	}
	RPCSession::OnShutdownSession();
}

void MapServerSession::PackMapServerInfo(INetPacket& pck) const
{
	pck << m_sn << m_gsIdx << m_host << m_port;
}

int MapServerSessionHandler::HandleRegister(MapServerSession *pSession, INetPacket &pck)
{
	if (pSession->GetProxy() != NULL) {
		WLOG("MapServerSession[%u] is already registered.", pSession->m_sn);
		return SessionHandleKill;
	}

	uint32 hintServiceId;
	pck >> hintServiceId >> pSession->m_service >> pSession->m_gsIdx
		>> pSession->m_host >> pSession->m_port;
	MapServerProxy* pMapServerProxy = new MapServerProxy(pSession);
	pSession->SetProxy(pMapServerProxy);
	if (!sMapServerMgr.RegisterMapServer(hintServiceId, pMapServerProxy)) {
		return SessionHandleKill;
	}

	NetPacket resp(GS_REGISTER_RESP);
	resp << pSession->GetServiceId() <<
		sCenterSession.GetServerId() << pSession->m_sn;
	pSession->PushSendPacket(resp);

	sMapServerMgr.StartWorldMapsOnServer(pMapServerProxy);
	NLOG("MapServer Register Success As [sn:%u serviceId:%u gsIdx:%u].",
		pSession->m_sn, pSession->m_serviceId, pSession->m_gsIdx);

	sCharacterMgr.SyncPlayerLevelMax2MapServer(pSession->m_serviceId);

	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleToSocialServerPacket(MapServerSession *pSession, INetPacket &pck)
{
	sSocialServerSession.PushSendPacket(pck.UnpackPacket());
	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleKick(MapServerSession *pSession, INetPacket &pck)
{
	ObjGUID playerGuid;
	int32 errCode;
	pck >> playerGuid >> errCode;

	Character* pChar = pSession->GetProxy()->GetCharacter(playerGuid);
	if (pChar != NULL) {
		sAccountMgr.KickAccount(pChar->acct, (GErrorCode)errCode);
	}

	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleCharacterSaved(MapServerSession *pSession, INetPacket &pck)
{
	ObjGUID playerGuid;
	int32 errCode;
	pck >> playerGuid >> errCode;

	if (errCode != RPCErrorNone) {
		WLOG("Save player(%u) instance failed, %d.", playerGuid.UID, errCode);
	}

	sTeleportMgr.HandleCharacterSaved(playerGuid);

	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleUpdateCharacterInfo(MapServerSession *pSession, INetPacket &pck)
{
	ObjGUID playerGuid;
	pck >> playerGuid;
	auto pChar = pSession->GetProxy()->GetCharacter(playerGuid);
	if (pChar == NULL) {
		return SessionHandleSuccess;
	}

	CBitMask& bitMask = pSession->sCharUpdateInfoBitMask;
	bitMask.Read(pck);

	auto pos = bitMask.FindFirst();
	for (; pos != CBitMask::npos; pos = bitMask.FindNext(pos)) {
		switch (CharUpdateInfo::Type(pos)) {
		case CharUpdateInfo::Type::VipLevel:
			pck >> pChar->lastVipLevel;
			break;
		case CharUpdateInfo::Type::Level:
			pck >> pChar->lastLevel >> pChar->rankVals.lastLevelTime;
			sCharacterMgr.UpdatePlayerLevelMax(pChar->lastLevel);
			sCharacterMgr.UpdateCharacterRankData(pChar, (int)RANK_SUBTYPE::PLAYER_LEVEL);
			break;
		case CharUpdateInfo::Type::PercHP:
			pck >> pChar->lastPercHP;
			bitMask.ClearDirty(pos);
			break;
		case CharUpdateInfo::Type::PercMP:
			pck >> pChar->lastPercMP;
			bitMask.ClearDirty(pos);
			break;
		case CharUpdateInfo::Type::Pos:
			pck >> pChar->lastPos;
			bitMask.ClearDirty(pos);
			break;
		case CharUpdateInfo::Type::FightValue:
			pck >> pChar->gsReadVals.lastFightValue >> pChar->rankVals.lastFightValueTime;
			sCharacterMgr.UpdateCharacterRankData(pChar, (int)RANK_SUBTYPE::PLAYER_FIGHT_VALUE);
			break;
		}
	}

	if (bitMask.IsDirty()) {
		NetPacket notify(CGX_UPDATE_CHARACTER_INFO);
		notify << pChar->guid.UID << pChar->guildId;
		bitMask.Write(notify);
		sSocialServerSession.BroadcastPacket2AllServices(notify);
	}

	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleSwitchMap(MapServerSession *pSession, INetPacket &pck)
{
	ObjGUID playerGuid;
	InstGUID instGuid;
	vector3f1f tgtPos;
	int32 tpType;
	uint32 tpFlags;
	std::string_view tpArgs;
	pck >> playerGuid >> instGuid >> tgtPos >> tpType >> tpFlags >> tpArgs;
	sTeleportMgr.HandleTeleportRequest(playerGuid, ObjGUID_NULL,
		instGuid, tgtPos, TeleportType(tpType), tpFlags, tpArgs);
	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleCharacterTeleportBeginEnterInstanceResult(MapServerSession *pSession, INetPacket &pck)
{
	int32 errCode;
	ObjGUID playerGuid, instOwner{};
	InstGUID instGuid{};
	vector3f1f tgtPos{};
	pck >> errCode >> playerGuid;
	if (errCode == CommonSuccess) {
		pck >> instOwner >> instGuid >> tgtPos;
	}
	sTeleportMgr.HandleBeginEnterInstanceRespon(
		GErrorCode(errCode), playerGuid, instOwner, instGuid, tgtPos);
	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleCharacterEnterMapResp(MapServerSession *pSession, INetPacket &pck)
{
	bool isSucc;
	ObjGUID playerGuid;
	pck >> isSucc >> playerGuid;
	if (!isSucc) {
		auto pChar = pSession->GetProxy()->GetCharacter(playerGuid);
		if (pChar != NULL) {
			sAccountMgr.KickAccount(pChar->acct, SessionKickInitDataError);
			WLOG("Player(%u,%s) enter map failed.", pChar->guid.UID, pChar->name.c_str());
		}
	}
	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleCharacterLeaveMapResp(MapServerSession *pSession, INetPacket &pck)
{
	bool isSucc;
	ObjGUID playerGuid;
	InstGUID instGuid;
	vector3f1f tgtPos;
	pck >> isSucc >> playerGuid >> instGuid >> tgtPos;
	sTeleportMgr.HandleLeaveMapRespon(isSucc, playerGuid, instGuid, tgtPos);
	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleUpdateActivityObjectPosition(MapServerSession *pSession, INetPacket &pck)
{
	sActivityObjectMgr.OnUpdateActivityObjectPosition(pck);
	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleQueryActivityObjectPosition(MapServerSession *pSession, INetPacket &pck, const RPCActor::RequestMetaInfo &info)
{
	sActivityObjectMgr.OnQueryActivityObjectPosition(pck, pSession, info);
	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleMapTeamCreate(MapServerSession *pSession, INetPacket &pck)
{
	InstGUID instGuid;
	uint32 teamId;
	pck >> instGuid >> teamId;
	Team* pTeam = sTeamMgr.GetTeam(teamId);
	if (pTeam != NULL) {
		pTeam->SendTeamInfoToInstance(instGuid);
	}
	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleMail(MapServerSession *pSession, INetPacket &pck)
{
	sMailMgr.HandleMailRequest(pck);
	return SessionHandleSuccess;
}

int MapServerSessionHandler::HandleFinishGuildLeague(MapServerSession *pSession, INetPacket &pck)
{
	InstGUID instGuid;
	uint32 winGuildId;
	int64 startTime;
	pck >> instGuid >> winGuildId >> startTime;
	if (winGuildId != 0) {
		sGuildLeagueMgr.OnFinishGuildLeague(instGuid, winGuildId, startTime);
	} else {
		auto guilds = sGuildLeagueMgr.GetGuildLeagueMember(instGuid, startTime);
		if (guilds.first != 0) {
			NetPacket rpcReqPck(CGX_RANK_GET_GUILD_RANK_VALUE);
			rpcReqPck << (u8)SocialRPCReply::GameServer << (u8)RANK_SUBTYPE::GUILD_FIGHT_VALUE;
			rpcReqPck << guilds.first << guilds.second;
			sSocialServerSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
				if (err != DBPErrorNone) {
					WLOG("Get Rank failed.");
					return;
				}
				uint32 winGuildId = 0;
				std::pair<uint32, uint32> ranks;
				pck >> ranks.first >> ranks.second;
				if (ranks.first != 0 && ranks.second != 0) {
					winGuildId = ranks.first < ranks.second ? guilds.first : guilds.second;
				} else if (ranks.first != 0) {
					winGuildId = guilds.first;
				} else if (ranks.second != 0) {
					winGuildId = guilds.second;
				}
				if (winGuildId != 0) {
					sGuildLeagueMgr.OnFinishGuildLeague(instGuid, winGuildId, startTime);
				}
			}, &sGameServer, DEF_S2S_RPC_TIMEOUT);
		}
	}
	return SessionHandleSuccess;
}